/*
 *  BitmapImage.cpp
 *  MondialiSciAlpinismo
 *
 *  Created by Giorgio Tino on 2/11/13.
 *  Copyright 2013 __MyCompanyName__. All rights reserved.
 *
 */

#include <stdio.h>

/* PLIB Includes */
#include <plib/ul.h>
#include <plib/pw.h>

#include "BitmapImage.h"

/* DEFINES */
#define EXIT_ERROR(x) printf(x);exit(0) 

const GLubyte alphaKeys[][3] = { {255,255,255}, {255,0,0}, {0,255,0}, {0,0,255} };

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

BitmapImage::BitmapImage() 
: hidden(false)
, x(0.0f)
, y(0.0f)
, dx(1.0f)
, dy(1.0f)
{
}

BitmapImage::BitmapImage(const char *filename, const eAlphaKey alphaKey, const float x, const float y, const float dx, const float dy)
: hidden(false)
{
    this->x = x;
    this->y = y;
    this->dx = dx;
    this->dy = dy;
	
	this->fileName = ulStrDup(filename);
	this->alphaKey = alphaKey;
	
    this->createTexture();
}

BitmapImage::~BitmapImage()
{
	//glDeleteTextures( 1, &texId );
	delete texture;
}

int BitmapImage::createTexture()
{
	int i;
	int j;
		
	BITMAPINFO *info;
	
	GLubyte *raw = LoadDIBitmap(this->fileName, &info);
	
	h = info->bmiHeader.biHeight;
	w = info->bmiHeader.biWidth;
	
	free(info);
	
	bh = pow2roundup(h);
	bw = pow2roundup(w);
	
	GLubyte *image = new GLubyte[bh*bw*4];
	
	int scan_line_completion = 0;
	int alpha_scan_line_correction = 0;
	int zero_padding = w%4;
	
	for(j = 0; j < h; j++)
	{
		for(i = 0; i < w*3; i += 3)
		{
			// RGB is reversed (BGR), alpha is not present, some zero padding to the
			// end of the scan line to be mult of 4 bytes (R+G+B = 3 bytes) and
			// some other stuff that I won't remember anymore...but now works fine!
			image[i+alpha_scan_line_correction+2+bw*4*j] = (GLubyte)(raw[i+2+scan_line_completion+j*w*3]);//printf("[%d %d][%d] %u\n",i,j,i+0+scan_line_completion+j*bw*4,this->image[i+alpha_scan_line_completionrection+2+bw*4*j]);
			image[i+alpha_scan_line_correction+1+bw*4*j] = (GLubyte)(raw[i+1+scan_line_completion+j*w*3]);//printf("[%d %d][%d] %u\n",i,j,i+1+scan_line_completion+j*bw*4,this->image[i+alpha_scan_line_completionrection+1+bw*4*j]);
			image[i+alpha_scan_line_correction+0+bw*4*j] = (GLubyte)(raw[i+0+scan_line_completion+j*w*3]);//printf("[%d %d][%d] %u\n",i,j,i+2+scan_line_completion+j*bw*4,this->image[i+alpha_scan_line_completionrection+0+bw*4*j]);          
			image[i+alpha_scan_line_correction+3+bw*4*j] = (GLubyte) 255;
			
			if(alphaKey != eAlphaKey_NONE)
				if( (image[i+alpha_scan_line_correction+2+bw*4*j] == alphaKeys[alphaKey][2]) &&
				   (image[i+alpha_scan_line_correction+1+bw*4*j] == alphaKeys[alphaKey][1]) &&
				   (image[i+alpha_scan_line_correction+0+bw*4*j] == alphaKeys[alphaKey][0]))
				{
					image[i+alpha_scan_line_correction+0+bw*4*j] = (GLubyte) 0;
					image[i+alpha_scan_line_correction+1+bw*4*j] = (GLubyte) 0;
					image[i+alpha_scan_line_correction+2+bw*4*j] = (GLubyte) 0;
					image[i+alpha_scan_line_correction+3+bw*4*j] = (GLubyte) 0;
				}
			
			alpha_scan_line_correction++;
		}
		scan_line_completion += zero_padding;
		alpha_scan_line_correction = 0;
	}
	
	free(raw);
	raw = NULL;
	
	texture = new ssgTexture (this->fileName, image, bw, bh, 4, TRUE, TRUE);
	
	//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//	
//	glGenTextures(1, &texId);
//	
	glBindTexture(GL_TEXTURE_2D, texture->getHandle());
	
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bw, bh, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
		
	//delete[] image;
	
	// Fit sub-texture to screen
	texCoordX = (float)w / (float)bw;
	texCoordY = (float)h / (float)bh;
	
	return 1;
}

void BitmapImage::display()
{
	this->update();
	
	if(this->hidden)
		return;
	
	/*Set up the 2D ambient */
	int fh = (float)glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ;
	int fw = (float)glutGet ( (GLenum) GLUT_WINDOW_WIDTH  ) ;
	
	glDisable      ( GL_LIGHTING ) ;
	glDisable      ( GL_DEPTH_TEST ) ;
	glDisable      ( GL_CULL_FACE  ) ;
	
	glEnable       ( GL_ALPHA_TEST ) ;
	glEnable       ( GL_BLEND ) ;
	glAlphaFunc    ( GL_GREATER, 0.1f ) ;
	glBlendFunc    ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
	
	//create an projection that acts like a 2D screen
	glMatrixMode   ( GL_PROJECTION ) ;
	glPushMatrix   () ;
	glLoadIdentity () ;
	glOrtho        ( 0.0, 1.0, 0.0, fw/fh, 0.0, 1.0 ) ;
	
	glMatrixMode   ( GL_MODELVIEW ) ;
	glPushMatrix   () ;
	glLoadIdentity () ;     
	
	// Draw the texture!!!
	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBindTexture(GL_TEXTURE_2D, texture->getHandle());
	glBegin(GL_QUADS);
	glTexCoord2f(0, 0); glVertex2f(x, y);
	glTexCoord2f(0, texCoordY); glVertex2f(x, y + dy);
	glTexCoord2f(texCoordX, texCoordY); glVertex2f(x + dx, y + dy);
	glTexCoord2f(texCoordX, 0); glVertex2f(x + dx, y);
	glEnd();
	glDisable(GL_TEXTURE_2D);
	
	/* Back to 3d view */
	
	glMatrixMode   ( GL_PROJECTION ) ;
	glPopMatrix    () ;
	glMatrixMode   ( GL_MODELVIEW ) ;
	glPopMatrix    () ;
	
	glDisable      ( GL_ALPHA_TEST ) ;
	glDisable      ( GL_BLEND ) ;
	glAlphaFunc    ( GL_ALWAYS, 0.0 ) ;
	glBlendFunc    ( GL_ONE, GL_ZERO ) ;
	
	glEnable     ( GL_LIGHTING ) ;
	glEnable     ( GL_DEPTH_TEST ) ;
	glEnable      ( GL_CULL_FACE  ) ;
}

bool BitmapImage::hitTest(int b, int s, int xCoord, int yCoord)
{
	int h = glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ;
	int w = glutGet ( (GLenum) GLUT_WINDOW_WIDTH  ) ;
	
	float xp = (float)xCoord / (float)w;
	float yp = (float)(h-yCoord) / (float)h;
	
	if(w > h)
	{
		xp *= 1.0;
		yp *= 1.0; 
	}
	else
    {
		xp *= h/w;
		yp *= 1.0;        
    }
	
	return (x <= xp) && (y <= yp) && (xp <= (x + dx)) && (yp <= (y + dy));
}	
